1 package org.apache.maven.surefire.junit4;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.surefire.booter.Command;
23 import org.apache.maven.surefire.booter.CommandListener;
24 import org.apache.maven.surefire.booter.CommandReader;
25 import org.apache.maven.surefire.common.junit4.ClassMethod;
26 import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
27 import org.apache.maven.surefire.common.junit4.JUnit4TestChecker;
28 import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
29 import org.apache.maven.surefire.common.junit4.Notifier;
30 import org.apache.maven.surefire.providerapi.AbstractProvider;
31 import org.apache.maven.surefire.providerapi.ProviderParameters;
32 import org.apache.maven.surefire.report.ConsoleOutputReceiver;
33 import org.apache.maven.surefire.report.PojoStackTraceWriter;
34 import org.apache.maven.surefire.report.ReportEntry;
35 import org.apache.maven.surefire.report.ReporterFactory;
36 import org.apache.maven.surefire.report.RunListener;
37 import org.apache.maven.surefire.report.SimpleReportEntry;
38 import org.apache.maven.surefire.suite.RunResult;
39 import org.apache.maven.surefire.testset.TestListResolver;
40 import org.apache.maven.surefire.testset.TestRequest;
41 import org.apache.maven.surefire.testset.TestSetFailedException;
42 import org.apache.maven.surefire.util.RunOrderCalculator;
43 import org.apache.maven.surefire.util.ScanResult;
44 import org.apache.maven.surefire.util.TestsToRun;
45 import org.junit.runner.Description;
46 import org.junit.runner.Request;
47 import org.junit.runner.Result;
48 import org.junit.runner.Runner;
49 import org.junit.runner.manipulation.Filter;
50 import org.junit.runner.notification.RunNotifier;
51 import org.junit.runner.notification.StoppedByUserException;
52
53 import java.util.Collection;
54 import java.util.Set;
55
56 import static java.lang.reflect.Modifier.isAbstract;
57 import static java.lang.reflect.Modifier.isInterface;
58 import static org.apache.maven.surefire.booter.CommandReader.getReader;
59 import static org.apache.maven.surefire.common.junit4.JUnit4ProviderUtil.generateFailingTests;
60 import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createDescription;
61 import static org.apache.maven.surefire.common.junit4.JUnit4Reflector.createIgnored;
62 import static org.apache.maven.surefire.common.junit4.JUnit4RunListener.rethrowAnyTestMechanismFailures;
63 import static org.apache.maven.surefire.common.junit4.JUnit4RunListenerFactory.createCustomListeners;
64 import static org.apache.maven.surefire.common.junit4.Notifier.pureNotifier;
65 import static org.apache.maven.surefire.report.ConsoleOutputCapture.startCapture;
66 import static org.apache.maven.surefire.report.SimpleReportEntry.withException;
67 import static org.apache.maven.surefire.testset.TestListResolver.optionallyWildcardFilter;
68 import static org.apache.maven.surefire.util.TestsToRun.fromClass;
69 import static org.junit.runner.Request.aClass;
70 import static org.junit.runner.Request.method;
71
72
73
74
75 public class JUnit4Provider
76 extends AbstractProvider
77 {
78 private static final String UNDETERMINED_TESTS_DESCRIPTION = "cannot determine test in forked JVM with surefire";
79
80 private final ClassLoader testClassLoader;
81
82 private final String customRunListeners;
83
84 private final JUnit4TestChecker jUnit4TestChecker;
85
86 private final TestListResolver testResolver;
87
88 private final ProviderParameters providerParameters;
89
90 private final RunOrderCalculator runOrderCalculator;
91
92 private final ScanResult scanResult;
93
94 private final int rerunFailingTestsCount;
95
96 private final CommandReader commandsReader;
97
98 private TestsToRun testsToRun;
99
100 public JUnit4Provider( ProviderParameters bootParams )
101 {
102
103 commandsReader = bootParams.isInsideFork() ? getReader().setShutdown( bootParams.getShutdown() ) : null;
104 providerParameters = bootParams;
105 testClassLoader = bootParams.getTestClassLoader();
106 scanResult = bootParams.getScanResult();
107 runOrderCalculator = bootParams.getRunOrderCalculator();
108 customRunListeners = bootParams.getProviderProperties().get( "listener" );
109 jUnit4TestChecker = new JUnit4TestChecker( testClassLoader );
110 TestRequest testRequest = bootParams.getTestRequest();
111 testResolver = testRequest.getTestListResolver();
112 rerunFailingTestsCount = testRequest.getRerunFailingTestsCount();
113 }
114
115 public RunResult invoke( Object forkTestSet )
116 throws TestSetFailedException
117 {
118 upgradeCheck();
119
120 ReporterFactory reporterFactory = providerParameters.getReporterFactory();
121
122 RunResult runResult;
123 try
124 {
125 RunListener reporter = reporterFactory.createReporter();
126
127 startCapture( (ConsoleOutputReceiver) reporter );
128
129
130 if ( testsToRun == null )
131 {
132 setTestsToRun( forkTestSet );
133 }
134
135 Notifier notifier = new Notifier( new JUnit4RunListener( reporter ), getSkipAfterFailureCount() );
136 Result result = new Result();
137 notifier.addListeners( createCustomListeners( customRunListeners ) )
138 .addListener( result.createListener() );
139
140 if ( isFailFast() && commandsReader != null )
141 {
142 registerPleaseStopJUnitListener( notifier );
143 }
144
145 try
146 {
147 notifier.fireTestRunStarted( testsToRun.allowEagerReading()
148 ? createTestsDescription( testsToRun )
149 : createDescription( UNDETERMINED_TESTS_DESCRIPTION ) );
150
151 if ( commandsReader != null )
152 {
153 registerShutdownListener( testsToRun );
154 commandsReader.awaitStarted();
155 }
156
157 for ( Class<?> testToRun : testsToRun )
158 {
159 executeTestSet( testToRun, reporter, notifier );
160 }
161 }
162 finally
163 {
164 notifier.fireTestRunFinished( result );
165 notifier.removeListeners();
166 }
167 rethrowAnyTestMechanismFailures( result );
168 }
169 finally
170 {
171 runResult = reporterFactory.close();
172 }
173 return runResult;
174 }
175
176 private void setTestsToRun( Object forkTestSet )
177 throws TestSetFailedException
178 {
179 if ( forkTestSet instanceof TestsToRun )
180 {
181 testsToRun = (TestsToRun) forkTestSet;
182 }
183 else if ( forkTestSet instanceof Class )
184 {
185 testsToRun = fromClass( (Class<?>) forkTestSet );
186 }
187 else
188 {
189 testsToRun = scanClassPath();
190 }
191 }
192
193 private boolean isRerunFailingTests()
194 {
195 return rerunFailingTestsCount > 0;
196 }
197
198 private boolean isFailFast()
199 {
200 return providerParameters.getSkipAfterFailureCount() > 0;
201 }
202
203 private int getSkipAfterFailureCount()
204 {
205 return isFailFast() ? providerParameters.getSkipAfterFailureCount() : 0;
206 }
207
208 private void registerShutdownListener( final TestsToRun testsToRun )
209 {
210 commandsReader.addShutdownListener( new CommandListener()
211 {
212 public void update( Command command )
213 {
214 testsToRun.markTestSetFinished();
215 }
216 } );
217 }
218
219 private void registerPleaseStopJUnitListener( final Notifier notifier )
220 {
221 commandsReader.addSkipNextTestsListener( new CommandListener()
222 {
223 public void update( Command command )
224 {
225 notifier.pleaseStop();
226 }
227 } );
228 }
229
230 private void executeTestSet( Class<?> clazz, RunListener reporter, Notifier notifier )
231 {
232 final ReportEntry report = new SimpleReportEntry( getClass().getName(), clazz.getName() );
233 reporter.testSetStarting( report );
234 try
235 {
236 executeWithRerun( clazz, notifier );
237 }
238 catch ( Throwable e )
239 {
240 if ( isFailFast() && e instanceof StoppedByUserException )
241 {
242 String reason = e.getClass().getName();
243 Description skippedTest = createDescription( clazz.getName(), createIgnored( reason ) );
244 notifier.fireTestIgnored( skippedTest );
245 }
246 else
247 {
248 String reportName = report.getName();
249 String reportSourceName = report.getSourceName();
250 PojoStackTraceWriter stackWriter = new PojoStackTraceWriter( reportSourceName, reportName, e );
251 reporter.testError( withException( reportSourceName, reportName, stackWriter ) );
252 }
253 }
254 finally
255 {
256 reporter.testSetCompleted( report );
257 }
258 }
259
260 private void executeWithRerun( Class<?> clazz, Notifier notifier )
261 throws TestSetFailedException
262 {
263 JUnitTestFailureListener failureListener = new JUnitTestFailureListener();
264 notifier.addListener( failureListener );
265 boolean hasMethodFilter = testResolver != null && testResolver.hasMethodPatterns();
266
267 try
268 {
269 try
270 {
271 notifier.asFailFast( isFailFast() );
272 execute( clazz, notifier, hasMethodFilter ? createMethodFilter() : null );
273 }
274 finally
275 {
276 notifier.asFailFast( false );
277 }
278
279
280 if ( isRerunFailingTests() )
281 {
282 Notifier rerunNotifier = pureNotifier();
283 notifier.copyListenersTo( rerunNotifier );
284 for ( int i = 0; i < rerunFailingTestsCount && !failureListener.getAllFailures().isEmpty(); i++ )
285 {
286 Set<ClassMethod> failedTests = generateFailingTests( failureListener.getAllFailures() );
287 failureListener.reset();
288 if ( !failedTests.isEmpty() )
289 {
290 executeFailedMethod( rerunNotifier, failedTests );
291 }
292 }
293 }
294 }
295 finally
296 {
297 notifier.removeListener( failureListener );
298 }
299 }
300
301 public Iterable<Class<?>> getSuites()
302 {
303 testsToRun = scanClassPath();
304 return testsToRun;
305 }
306
307 private TestsToRun scanClassPath()
308 {
309 final TestsToRun scannedClasses = scanResult.applyFilter( jUnit4TestChecker, testClassLoader );
310 return runOrderCalculator.orderTestClasses( scannedClasses );
311 }
312
313 private void upgradeCheck()
314 throws TestSetFailedException
315 {
316 if ( isJUnit4UpgradeCheck() )
317 {
318 Collection<Class<?>> classesSkippedByValidation =
319 scanResult.getClassesSkippedByValidation( jUnit4TestChecker, testClassLoader );
320 if ( !classesSkippedByValidation.isEmpty() )
321 {
322 StringBuilder reason = new StringBuilder();
323 reason.append( "Updated check failed\n" );
324 reason.append( "There are tests that would be run with junit4 / surefire 2.6 but not with [2.7,):\n" );
325 for ( Class testClass : classesSkippedByValidation )
326 {
327 reason.append( " " );
328 reason.append( testClass.getName() );
329 reason.append( "\n" );
330 }
331 throw new TestSetFailedException( reason.toString() );
332 }
333 }
334 }
335
336 static Description createTestsDescription( Iterable<Class<?>> classes )
337 {
338
339 Description description = createDescription( "null" );
340 for ( Class<?> clazz : classes )
341 {
342 description.addChild( createDescription( clazz.getName() ) );
343 }
344 return description;
345 }
346
347 private static boolean isJUnit4UpgradeCheck()
348 {
349 return System.getProperty( "surefire.junit4.upgradecheck" ) != null;
350 }
351
352 private static void execute( Class<?> testClass, Notifier notifier, Filter filter )
353 {
354 final int classModifiers = testClass.getModifiers();
355 if ( !isAbstract( classModifiers ) && !isInterface( classModifiers ) )
356 {
357 Request request = aClass( testClass );
358 if ( filter != null )
359 {
360 request = request.filterWith( filter );
361 }
362 Runner runner = request.getRunner();
363 if ( countTestsInRunner( runner.getDescription() ) != 0 )
364 {
365 runner.run( notifier );
366 }
367 }
368 }
369
370 private void executeFailedMethod( RunNotifier notifier, Set<ClassMethod> failedMethods )
371 throws TestSetFailedException
372 {
373 for ( ClassMethod failedMethod : failedMethods )
374 {
375 try
376 {
377 Class<?> methodClass = Class.forName( failedMethod.getClazz(), true, testClassLoader );
378 String methodName = failedMethod.getMethod();
379 method( methodClass, methodName ).getRunner().run( notifier );
380 }
381 catch ( ClassNotFoundException e )
382 {
383 throw new TestSetFailedException( "Unable to create test class '" + failedMethod.getClazz() + "'", e );
384 }
385 }
386 }
387
388
389
390
391
392
393
394
395 private static int countTestsInRunner( Description description )
396 {
397 if ( description.isSuite() )
398 {
399 int count = 0;
400 for ( Description child : description.getChildren() )
401 {
402 if ( !hasFilteredOutAllChildren( child ) )
403 {
404 count += countTestsInRunner( child );
405 }
406 }
407 return count;
408 }
409 else if ( description.isTest() )
410 {
411 return hasFilteredOutAllChildren( description ) ? 0 : 1;
412 }
413 else
414 {
415 return 0;
416 }
417 }
418
419 private static boolean hasFilteredOutAllChildren( Description description )
420 {
421 String name = description.getDisplayName();
422
423 if ( name == null )
424 {
425 return true;
426 }
427 else
428 {
429 name = name.trim();
430 return name.startsWith( "initializationError0(org.junit.runner.manipulation.Filter)" )
431 || name.startsWith( "initializationError(org.junit.runner.manipulation.Filter)" );
432 }
433 }
434
435 private Filter createMethodFilter()
436 {
437 TestListResolver methodFilter = optionallyWildcardFilter( testResolver );
438 return methodFilter.isEmpty() || methodFilter.isWildcard() ? null : new TestResolverFilter( methodFilter );
439 }
440 }